// SQLiteMap.cc
// author: dipina@eside.deusto.es
// compile: g++ SQLiteMap.cc -lsqlite3 -o SQLiteMap
#include "SQLiteMap.h"

namespace PracticaCaso {
	SQLiteMap::SQLiteMap(string fn): fileName(fn), dbh(0) {
		// Process the contents of the mapping file
		this->loadMappings(fn);
	}

	SQLiteMap::SQLiteMap(const SQLiteMap& rhs) {
		fileName = rhs.fileName;
		dbh = rhs.dbh;
		this->loadMappings(fileName);
	}

	SQLiteMap::~SQLiteMap() {
		cout << "SQLiteMap: destructor called " << endl;
		this->close();
	}

	void SQLiteMap::loadMappings(string mappingsDBFileName) {
		// Loads the mappings stored at SQLite DB into the map loadMappings
		// In the case that the DB does not exist, create it, its structure is given by file KeyValueDB.sql
		// If a select * from KeyValuePair executed through a sqlite3_get_table does not return SQLITE_OK, it means that the table does not exist, and needs being created
		// If there are unexpected error exit the program with exit(1)
		cout << "Opening DnsMap database ..." << endl;
		if (sqlite3_open(mappingsDBFileName.c_str(), &dbh) != SQLITE_OK) //c_str()porque hay que convertir de string a char*// la open si no existe BD la crea , si existe la abre.,
		{
			cerr << "Can't open database: " << sqlite3_errmsg(dbh) << endl;
			sqlite3_close(dbh);
			exit(1);
		
		}
		
		char **result;//puntero de arrays de strings
		int nrow;
		int ncol;
		char *errorMsg;
		cout << "Checking if DnsMap table already exists ..." << endl;
		if (sqlite3_get_table(dbh, "select * from KeyValuePair", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) {
			cerr << errorMsg << endl;
			sqlite3_free(errorMsg);
			if (sqlite3_get_table(dbh, "create table KeyValuePair (key_element BLOB NOT NULL PRIMARY KEY, value_element BLOB)", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
			{
				cerr << errorMsg << endl;
				sqlite3_free(errorMsg);
				sqlite3_close(dbh);
				exit(1);
			} else {
				cout << "Table DnsMap created" << endl;
				sqlite3_free_table(result);
			}
		} 
		
		cout << "Checking if there is at least one row in KeyValuePair ..." << endl;
		if (nrow == 0) {
			if (sqlite3_get_table(dbh, "insert into KeyValuePair(key_element, value_element) values ('deusto.es',  '127.0.0.1:1234')", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
			{
				cerr << errorMsg << endl;
				sqlite3_free(errorMsg);
				sqlite3_close(dbh);
				exit(1);
			} else {
				cout << "Row created: " << nrow << endl;
				sqlite3_free_table(result);
			}
		}
		
		//a partir de aqui ya sabemos que existe y hay datos en la bd
		
		cout << "Listing contents of KeyValuePair ..." << endl;
		if (sqlite3_get_table(dbh, "select * from KeyValuePair", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
		{ 
			cerr << errorMsg << endl;
			sqlite3_free(errorMsg);
		} else {
			// sqlite3 returns one extra row with the column headers
			for (int i=0; i<=nrow; i++) 
			{
				cout << string(result[i*ncol]) + "--" + string(result[i*ncol+1]) << endl;
			    dns2IpPortMap.insert(map<string,string>::value_type(string(result[i*ncol]),string(result[i*ncol+1])));
				//insertamos en nuestro mapa un map de string ,string , value_Type es el resultado de columna(dominio) y columna+1(ip:port) casting a string
			}
			sqlite3_free_table(result);
		}
		
		cout << "Closing KeyValuePair database ..." << endl;
		//sqlite3_close(dbh);
		//NO HAY QUE CERRAR LA BD , PORQUE CADA VEZ QUE ME LLEGUE UNA PETICION HAY QUE ALMACENARLA.
	
	}

	
	map<string, string> SQLiteMap::getMap() {
		return dns2IpPortMap;
	}


	string SQLiteMap::get(string key) 
	{
		string value;//se inicializa por defecto a ""
		map<string, string>::iterator it = dns2IpPortMap.find(key);
		if (it != dns2IpPortMap.end())
		{
			value = it->second;
		}
		return value;
	}

	void SQLiteMap::set(string mapKey, string mapValue)
	{
		// Undertake the update of the STL map and the database. Bear in mind that it there is not an entry with a passed key an INSERT will have to be executed, if there was already such an entry an UPDATE will take place
		map<string, string>::iterator it =dns2IpPortMap.find(mapKey);
		char **result;
		int nrow;
		int ncol;
		char *errorMsg;
		if (it != dns2IpPortMap.end())
		{
			it->second = mapValue;
			// Execute the Update on the database
			//                     "update KeyValuePair set value_element = '"  + mapKey + "' where key_element = '" + mapValue + " ' ";
			string sent = "update KeyValuePair set value_element = '"+mapValue+"' where key_element = '"+mapKey +" ' ";
			if(sqlite3_get_table(dbh, sent.c_str(), &result, &nrow, &ncol, &errorMsg)!=SQLITE_OK)
			{
				
				cerr << errorMsg << endl;
				sqlite3_free(errorMsg);
				sqlite3_close(dbh);
				exit(1);
			}
		}
		else
		{
			dns2IpPortMap.insert(map<string,string>::value_type(mapKey, mapValue));
			// Execute the insert on the database
			string sent = "insert into KeyValuePair(key_element, value_element) values ('"+mapKey+"',  '"+mapValue+"')";
			if (sqlite3_get_table(dbh,sent.c_str(), &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) {
				cerr << errorMsg << endl;
				sqlite3_free(errorMsg);
				sqlite3_close(dbh);
				exit(1);
			}
		}
		
		
	
	}


	void SQLiteMap::close() {
		// Close the database properly
		sqlite3_close(dbh);
		dbh=0;
	}

	ostream & operator << (ostream & os, SQLiteMap &t) {
		os << "DB file name: " << t.fileName << endl;
		os << "DNS mappings:" << endl;
		typedef map<string, string>::const_iterator CI;
		for (CI p = t.dns2IpPortMap.begin(); p != t.dns2IpPortMap.end(); ++p) {
			os <<  p->first << " : " << p->second << endl;
		}
		return os;
	}
}


/*
// global variable
PracticaCaso::SQLiteMap * SQLiteMap_pointer;

// function called when CTRL-C is pressed
void ctrl_c(int)
{
    printf("\nCTRL-C was pressed...\n");
	delete SQLiteMap_pointer;
}


void usage() {
	cout << "Usage: SQLiteMap <name-mappings-db-file>" << endl;
	exit(1);
}

int main(int argc, char** argv) {
	signal(SIGINT,ctrl_c);

	if (argc != 2) {
		usage();
	}

	PracticaCaso::SQLiteMap * SQLiteMap = new PracticaCaso::SQLiteMap((string)argv[1]);
	cout << "SQLiteMap instance: " << endl << SQLiteMap << endl;
	//SQLiteMap_pointer = &SQLiteMap;
	SQLiteMap_pointer = SQLiteMap;

	//SQLiteMap.set("saludo", "hola");
	//SQLiteMap.set("despedida", "adios");
	SQLiteMap->set("saludo", "hola");
	SQLiteMap->set("despedida", "adios");

	//cout << "SQLiteMap[\"saludo\"] = " << SQLiteMap.get("saludo") << endl;
	//cout << "SQLiteMap[\"despedida\"] = " << SQLiteMap.get("despedida") << endl;
	cout << "SQLiteMap[\"saludo\"] = " << SQLiteMap->get("saludo") << endl;
	cout << "SQLiteMap[\"despedida\"] = " << SQLiteMap->get("despedida") << endl;

	cout << "SQLiteMap instance after 2 sets: " << endl << *SQLiteMap << endl;

	//map<string, string> savedSQLiteMap = SQLiteMap.getMap();
	map<string, string> savedSQLiteMap = SQLiteMap->getMap();
	cout << "Recuperado el mapa" << endl;
	typedef map<string, string>::const_iterator CI;
	for (CI p = savedSQLiteMap.begin(); p != savedSQLiteMap.end(); ++p) {
		cout << "dns2IpPortMap[" << p->first << "] = " << p->second << endl;
	}
	
	delete SQLiteMap_pointer;
}
*/

